package com.zifan.sever.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zifan.sever.bean.Admin;
import com.zifan.sever.bean.AdminRole;
import com.zifan.sever.bean.Role;
import com.zifan.sever.config.minio.MinioProperties;
import com.zifan.sever.config.minio.MinioTemplate;
import com.zifan.sever.config.security.JwtTokenUtils;
import com.zifan.sever.mapper.AdminMapper;
import com.zifan.sever.mapper.AdminRoleMapper;
import com.zifan.sever.mapper.RoleMapper;
import com.zifan.sever.service.IAdminService;
import com.zifan.sever.utils.AdminUtil;
import com.zifan.sever.utils.FileUtil;
import com.zifan.sever.utils.RespBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zifan
 * @since 2021-04-10
 */
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {

    /**
     * spring security 中判断用户名的方法
     */
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AdminRoleMapper adminRoleMapper;

    /**
     * spring security 中判断密码的方法
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private JwtTokenUtils tokenUtils;

    @Autowired
    private RoleMapper roleMapper;

    /**
     * token的头部信息，在配置文件中
     */
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    private FileUtil fileUtil;

    /**
     * 判断登录，成功返回token
     * @param username
     * @param password
     * @param request
     * @return
     */
    @Override
    public RespBean login(String username, String password, String code, HttpServletRequest request) {
        //判断验证码
        String targetCode = (String) request.getSession().getAttribute("captcha");

//        if (StrUtil.isEmpty(code) || !targetCode.equalsIgnoreCase(code)) {
//            return RespBean.error("验证码错误！");
//        }

        //spring security 中判断用户名是否存在
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        /**
         * 如果userdetails为空 或者 前端传过来的密码和userdetails中不一样
         */
        if (ObjectUtil.isNull(userDetails) || !passwordEncoder.matches(password, userDetails.getPassword())) {
            return RespBean.error("用户名或密码不正确");
        }

        /**
         * 判断账号是否被禁用
         */
        if (!userDetails.isEnabled()) {
            return RespBean.error("账号被禁用，请联系管理员");
        }

        //登录成功，更新security登录用户对象
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

        // 登录成功 生成token
        String token = tokenUtils.generateToken(userDetails);
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        //我他妈服了，这里是tokenHead。这起名我真是操了
        map.put("tokenHead", tokenHead);

        return RespBean.success("登录成功", map);
    }

    @Override
    public Admin getAdminByUsername(String username) {
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username).eq("enabled", true);
        return this.getOne(wrapper);
    }

    @Override
    public List<Role> getRolesByAdminId(Integer id) {
        return roleMapper.getRolesByAdminId(id);
    }

    @Override
    public List<Admin> getAllAdmins(String key) {
        Admin admin = AdminUtil.getCurrent();
        return this.baseMapper.getAllAdmins(admin.getId(),key);
    }

    @Override
    public RespBean updateAdmins(Admin admin) {
        if (this.updateById(admin)){
            return RespBean.success("更新成功");
        }
        return RespBean.error("更新失败");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespBean deleteAdmins(Integer id) {
        if (this.removeById(id)){
            return RespBean.success("删除成功");
        }
        return RespBean.error("删除失败");
    }

    /**
     * 更新操作员的角色
     * @param adminId
     * @param rids
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespBean updateAdminRoles(Integer adminId, Integer[] rids) {
        //1,先删除这个操作员的所有角色
        QueryWrapper<AdminRole> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(AdminRole::getAdminId,adminId);
        adminRoleMapper.delete(wrapper);

        //2，添加前端传过来的ids
        Integer res = adminRoleMapper.addAdminRoles(adminId, rids);
        if (rids != null && rids.length == res){
            return RespBean.success("更新成功");
        }

        return RespBean.success("更新失败");
    }

    /**
     * 更新用户信息
     * @param admin
     * @param authentication
     * @return
     */
    @Override
    public RespBean updateAdminInfo(Admin admin, Authentication authentication) {
        if (this.updateById(admin)){
            //更新成功后需要设置security的全局上下文对象
            UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(admin,null,authentication.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(newAuth);
            return RespBean.success("更新成功");
        }
        return RespBean.error("更新失败");
    }

    /**
     * 更新用户密码
     * @param oldPass 旧密码
     * @param pass 新密码
     * @param adminId 用户id
     * @return
     */
    @Override
    public RespBean updateAdminPassword(String oldPass,String pass,Integer adminId) {
        Admin admin = this.getById(adminId);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (encoder.matches(oldPass,admin.getPassword())){
            //匹配成功
            admin.setPassword(encoder.encode(pass));
            if (this.updateById(admin)) {
                return RespBean.success("更新密码成功!");
            }else {
                return RespBean.error("更新密码失败！");
            }
        }
        return RespBean.error("旧密码输入错误,请重试！");
    }

    /**
     * 更新用户头像 这里使用minio实现
     * 目的，上传用户头像，返回该头像的url地址
     * @param file
     * @param id
     * @param authentication
     * @return
     */
    @Override
    public RespBean updateUsetFace(MultipartFile file, Integer id, Authentication authentication) {
        MinioProperties minioProperties = fileUtil.getMinioProperties();
        MinioTemplate minioTemplate = fileUtil.getMinioTemplate();
        String fileName = fileUtil.changeFileName(file.getOriginalFilename());
        String bucket = minioProperties.getBucket();
        try {
            minioTemplate.putObject(bucket,fileName,file.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileUrl = minioProperties.getUrl() + "/" +bucket +fileName;
        //还需要修改数据库中的地址
        Admin admin = this.getById(id);
        admin.setUserFace(fileUrl);
        if (this.updateById(admin)){
            //更新成功
            Admin principal = (Admin) authentication.getPrincipal();
            principal.setUserFace(fileUrl);
            //并且重新设置上下文对象
            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(admin,null,authentication.getAuthorities()));
            return RespBean.success("更新成功",fileUrl);
        }
        return RespBean.error("更新失败",fileUrl);
    }
}
